//
//  UIImage+Extension.swift
//  Social
//
//  Created by 李响 on 2019/5/31.
//  Copyright © 2019 shengsheng. All rights reserved.
//

import UIKit
import CoreImage
import Photos

public extension UIImage {
    
    func compressedQuality(_ maxLength: UInt64 = 1024 * 1024) -> UIImage? {
        guard
            var data = jpegData(compressionQuality: 1),
            data.count > maxLength else {
            return self
        }
        
        var max: CGFloat = 1
        var min: CGFloat = 0
        var compression: CGFloat = 1
        for _ in 0 ..< 6 {
            compression = (max + min) / 2
            data = jpegData(compressionQuality: compression) ?? Data()
            
            if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
                min = compression
            } else if data.count > maxLength {
                max = compression
            } else {
                break
            }
        }
        return UIImage(data: data, scale: scale)
    }
    
    func compressedSize(_ maxLength: UInt64 = 1024 * 1024) -> UIImage? {
        guard
            var data = jpegData(compressionQuality: 1),
            data.count > maxLength else {
            return self
        }
        
        var result = self
        var lastDataLength = 0
        while data.count > maxLength, data.count != lastDataLength {
            lastDataLength = data.count
            let ratio = CGFloat(maxLength) / CGFloat(data.count)
            let size = CGSize(width: Int(result.size.width * sqrt(ratio)),
                              height: Int(result.size.height * sqrt(ratio)))
            UIGraphicsBeginImageContext(size)
            result.draw(in: CGRect(origin: .zero, size: size))
            result = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            data = result.jpegData(compressionQuality: 1) ?? Data()
        }
        return result
    }
    
    func compressed(maxLength: UInt64 = 1024 * 1024) -> UIImage? {
        guard let image = compressedQuality(maxLength) else {
            return self
        }
        guard let result = image.compressedSize(maxLength) else {
            return self
        }
        return result
    }
}

extension UIImage {
    
    func toCGImage() -> CGImage? {
        return cgImage ?? ciImage?.toCGImage()
    }
    
    func toCIImage() -> CIImage? {
        return ciImage ?? cgImage?.toCIImage()
    }
}

extension CIImage {
    
    func toCGImage() -> CGImage? {
        return CIContext().createCGImage(self, from: extent)
    }
}

extension CGImage {
    
    func toCIImage() -> CIImage? {
        return CIImage(cgImage: self)
    }
}

extension UIImage {
    
    func saveToPhotoLibrary(_ completionHandler: ((Result<Bool, UIImage.Error>) -> Void)? = nil) {
        let authorizationStatus = PHPhotoLibrary.authorizationStatus()
        
        switch authorizationStatus {
        case .authorized:
            save(completionHandler)
            
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization { (status) in
                if status == .authorized {
                    self.save(completionHandler)
                    
                } else {
                    completionHandler?(.failure(.authorization(status: status)))
                }
            }
            
        default:
            completionHandler?(.failure(.authorization(status: authorizationStatus)))
        }
    }
    
    private func save(_ completionHandler: ((Result<Bool, UIImage.Error>) -> Void)? = nil) {
        var data = self.kf.gifRepresentation()
        
        if data == nil {
            data = self.kf.jpegRepresentation(compressionQuality: 1)
        }
        
        if data == nil {
            data = self.kf.pngRepresentation()
        }
        
        guard let imageData = data else {
            completionHandler?(.failure(.unsupported))
            return
        }
        
        let library = PHPhotoLibrary.shared()
        
        library.performChanges({
            PHAssetCreationRequest.forAsset().addResource(with: .photo, data: imageData, options: nil)
            
        }) { (success, error) in
            DispatchQueue.main.async {
                if let error = error {
                    completionHandler?(.failure(.photoLibrary(error: error)))
                    
                } else {
                    completionHandler?(.success(true))
                }
            }
        }
    }
    
    enum Error: Swift.Error {
        case authorization(status: PHAuthorizationStatus)
        case unsupported
        case photoLibrary(error: Swift.Error)
    }
}

